/*-------------------------------------------------------------------------
 *
 * nodeBitmapIndexscan.c
 *      Routines to support bitmapped index scans of relations
 *
 * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * This source code file contains modifications made by THL A29 Limited ("Tencent Modifications").
 * All Tencent Modifications are Copyright (C) 2023 THL A29 Limited.
 * 
 * IDENTIFICATION
 *      src/backend/executor/nodeBitmapIndexscan.c
 *
 *-------------------------------------------------------------------------
 */
/*
 * INTERFACE ROUTINES
 *        MultiExecBitmapIndexScan    scans a relation using index.
 *        ExecInitBitmapIndexScan        creates and initializes state info.
 *        ExecReScanBitmapIndexScan    prepares to rescan the plan.
 *        ExecEndBitmapIndexScan        releases all storage.
 */
#include "postgres.h"

#include "executor/execdebug.h"
#include "executor/nodeBitmapIndexscan.h"
#include "executor/nodeIndexscan.h"
#include "miscadmin.h"
#include "utils/memutils.h"

#ifdef __AUDIT_FGA__
#include "audit/audit_fga.h"
#endif

/* ----------------------------------------------------------------
 *        ExecBitmapIndexScan
 *
 *        stub for pro forma compliance
 * ----------------------------------------------------------------
 */
static TupleTableSlot *
ExecBitmapIndexScan(PlanState *pstate)
{
    elog(ERROR, "BitmapIndexScan node does not support ExecProcNode call convention");
    return NULL;
}

/* ----------------------------------------------------------------
 *        MultiExecBitmapIndexScan(node)
 * ----------------------------------------------------------------
 */
Node *
MultiExecBitmapIndexScan(BitmapIndexScanState *node)
{// #lizard forgives
    TIDBitmap  *tbm;
    IndexScanDesc scandesc;
    double        nTuples = 0;
    bool        doscan;

    /* must provide our own instrumentation support */
    if (node->ss.ps.instrument)
        InstrStartNode(node->ss.ps.instrument);

    /*
     * extract necessary information from index scan node
     */
    scandesc = node->biss_ScanDesc;

    /*
     * If we have runtime keys and they've not already been set up, do it now.
     * Array keys are also treated as runtime keys; note that if ExecReScan
     * returns with biss_RuntimeKeysReady still false, then there is an empty
     * array key so we should do nothing.
     */
    if (!node->biss_RuntimeKeysReady &&
        (node->biss_NumRuntimeKeys != 0 || node->biss_NumArrayKeys != 0))
    {
        ExecReScan((PlanState *) node);
        doscan = node->biss_RuntimeKeysReady;
    }
    else
        doscan = true;

    /*
     * Prepare the result bitmap.  Normally we just create a new one to pass
     * back; however, our parent node is allowed to store a pre-made one into
     * node->biss_result, in which case we just OR our tuple IDs into the
     * existing bitmap.  (This saves needing explicit UNION steps.)
     */
    if (node->biss_result)
    {
        tbm = node->biss_result;
        node->biss_result = NULL;    /* reset for next time */
    }
    else
    {
        /* XXX should we use less than work_mem for this? */
        tbm = tbm_create(work_mem * 1024L,
                         ((BitmapIndexScan *) node->ss.ps.plan)->isshared ?
                         node->ss.ps.state->es_query_dsa : NULL);
    }

    /*
     * Get TIDs from index and insert into bitmap
     */
    while (doscan)
    {
        nTuples += (double) index_getbitmap(scandesc, tbm);

        CHECK_FOR_INTERRUPTS();

        doscan = ExecIndexAdvanceArrayKeys(node->biss_ArrayKeys,
                                           node->biss_NumArrayKeys);
        if (doscan)                /* reset index scan */
            index_rescan(node->biss_ScanDesc,
                         node->biss_ScanKeys, node->biss_NumScanKeys,
                         NULL, 0);
    }

    /* must provide our own instrumentation support */
    if (node->ss.ps.instrument)
        InstrStopNode(node->ss.ps.instrument, nTuples);

    return (Node *) tbm;
}

/* ----------------------------------------------------------------
 *        ExecReScanBitmapIndexScan(node)
 *
 *        Recalculates the values of any scan keys whose value depends on
 *        information known at runtime, then rescans the indexed relation.
 * ----------------------------------------------------------------
 */
void
ExecReScanBitmapIndexScan(BitmapIndexScanState *node)
{
    ExprContext *econtext = node->biss_RuntimeContext;

    /*
     * Reset the runtime-key context so we don't leak memory as each outer
     * tuple is scanned.  Note this assumes that we will recalculate *all*
     * runtime keys on each call.
     */
    if (econtext)
        ResetExprContext(econtext);

    /*
     * If we are doing runtime key calculations (ie, any of the index key
     * values weren't simple Consts), compute the new key values.
     *
     * Array keys are also treated as runtime keys; note that if we return
     * with biss_RuntimeKeysReady still false, then there is an empty array
     * key so no index scan is needed.
     */
    if (node->biss_NumRuntimeKeys != 0)
        ExecIndexEvalRuntimeKeys(econtext,
                                 node->biss_RuntimeKeys,
                                 node->biss_NumRuntimeKeys);
    if (node->biss_NumArrayKeys != 0)
        node->biss_RuntimeKeysReady =
            ExecIndexEvalArrayKeys(econtext,
                                   node->biss_ArrayKeys,
                                   node->biss_NumArrayKeys);
    else
        node->biss_RuntimeKeysReady = true;

    /* reset index scan */
    if (node->biss_RuntimeKeysReady)
        index_rescan(node->biss_ScanDesc,
                     node->biss_ScanKeys, node->biss_NumScanKeys,
                     NULL, 0);
}

/* ----------------------------------------------------------------
 *        ExecEndBitmapIndexScan
 * ----------------------------------------------------------------
 */
void
ExecEndBitmapIndexScan(BitmapIndexScanState *node)
{
    Relation    indexRelationDesc;
    IndexScanDesc indexScanDesc;

    /*
     * extract information from the node
     */
    indexRelationDesc = node->biss_RelationDesc;
    indexScanDesc = node->biss_ScanDesc;

    /*
     * Free the exprcontext ... now dead code, see ExecFreeExprContext
     */
#ifdef NOT_USED
    if (node->biss_RuntimeContext)
        FreeExprContext(node->biss_RuntimeContext, true);
#endif

    /*
     * close the index relation (no-op if we didn't open it)
     */
    if (indexScanDesc)
        index_endscan(indexScanDesc);
    if (indexRelationDesc)
        index_close(indexRelationDesc, NoLock);
}

/* ----------------------------------------------------------------
 *        ExecInitBitmapIndexScan
 *
 *        Initializes the index scan's state information.
 * ----------------------------------------------------------------
 */
BitmapIndexScanState *
ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags)
{// #lizard forgives
    BitmapIndexScanState *indexstate;
    bool        relistarget;

#ifdef __AUDIT_FGA__
    ListCell      *item;
#endif    

    /* check for unsupported flags */
    Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));

    /*
     * create state structure
     */
    indexstate = makeNode(BitmapIndexScanState);
    indexstate->ss.ps.plan = (Plan *) node;
    indexstate->ss.ps.state = estate;
    indexstate->ss.ps.ExecProcNode = ExecBitmapIndexScan;

    /* normally we don't make the result bitmap till runtime */
    indexstate->biss_result = NULL;

    /*
     * Miscellaneous initialization
     *
     * We do not need a standard exprcontext for this node, though we may
     * decide below to create a runtime-key exprcontext
     */

    /*
     * initialize child expressions
     *
     * We don't need to initialize targetlist or qual since neither are used.
     *
     * Note: we don't initialize all of the indexqual expression, only the
     * sub-parts corresponding to runtime keys (see below).
     */

    /*
     * We do not open or lock the base relation here.  We assume that an
     * ancestor BitmapHeapScan node is holding AccessShareLock (or better) on
     * the heap relation throughout the execution of the plan tree.
     */

    indexstate->ss.ss_currentRelation = NULL;
    indexstate->ss.ss_currentScanDesc = NULL;

    /*
     * If we are just doing EXPLAIN (ie, aren't going to run the plan), stop
     * here.  This allows an index-advisor plugin to EXPLAIN a plan containing
     * references to nonexistent indexes.
     */
    if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
        return indexstate;

    /*
     * Open the index relation.
     *
     * If the parent table is one of the target relations of the query, then
     * InitPlan already opened and write-locked the index, so we can avoid
     * taking another lock here.  Otherwise we need a normal reader's lock.
     */
    relistarget = ExecRelationIsTargetRelation(estate, node->scan.scanrelid);
    indexstate->biss_RelationDesc = index_open(node->indexid,
                                               relistarget ? NoLock : AccessShareLock);

    /*
     * Initialize index-specific scan state
     */
    indexstate->biss_RuntimeKeysReady = false;
    indexstate->biss_RuntimeKeys = NULL;
    indexstate->biss_NumRuntimeKeys = 0;

#ifdef __AUDIT_FGA__
    if (enable_fga)
    {
        foreach (item, node->scan.plan.audit_fga_quals)
        {
            AuditFgaPolicy *audit_fga_qual = (AuditFgaPolicy *) lfirst(item);
            
            audit_fga_policy_state * audit_fga_policy_state_item
                    = palloc0(sizeof(audit_fga_policy_state));

            audit_fga_policy_state_item->policy_name = audit_fga_qual->policy_name;
            audit_fga_policy_state_item->query_string = audit_fga_qual->query_string;
            audit_fga_policy_state_item->qual = 
                ExecInitQual(audit_fga_qual->qual, (PlanState *) indexstate);

            indexstate->ss.ps.audit_fga_qual = 
                lappend(indexstate->ss.ps.audit_fga_qual, audit_fga_policy_state_item);      
        }
    }
#endif    

    /*
     * build the index scan keys from the index qualification
     */
    ExecIndexBuildScanKeys((PlanState *) indexstate,
                           indexstate->biss_RelationDesc,
                           node->indexqual,
                           false,
                           &indexstate->biss_ScanKeys,
                           &indexstate->biss_NumScanKeys,
                           &indexstate->biss_RuntimeKeys,
                           &indexstate->biss_NumRuntimeKeys,
                           &indexstate->biss_ArrayKeys,
                           &indexstate->biss_NumArrayKeys);

    /*
     * If we have runtime keys or array keys, we need an ExprContext to
     * evaluate them. We could just create a "standard" plan node exprcontext,
     * but to keep the code looking similar to nodeIndexscan.c, it seems
     * better to stick with the approach of using a separate ExprContext.
     */
    if (indexstate->biss_NumRuntimeKeys != 0 ||
        indexstate->biss_NumArrayKeys != 0)
    {
        ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;

        ExecAssignExprContext(estate, &indexstate->ss.ps);
        indexstate->biss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
        indexstate->ss.ps.ps_ExprContext = stdecontext;
    }
    else
    {
        indexstate->biss_RuntimeContext = NULL;
    }

    /*
     * Initialize scan descriptor.
     */
    indexstate->biss_ScanDesc =
        index_beginscan_bitmap(indexstate->biss_RelationDesc,
                               estate->es_snapshot,
                               indexstate->biss_NumScanKeys);

    /*
     * If no run-time keys to calculate, go ahead and pass the scankeys to the
     * index AM.
     */
    if (indexstate->biss_NumRuntimeKeys == 0 &&
        indexstate->biss_NumArrayKeys == 0)
        index_rescan(indexstate->biss_ScanDesc,
                     indexstate->biss_ScanKeys, indexstate->biss_NumScanKeys,
                     NULL, 0);

    /*
     * all done.
     */
    return indexstate;
}
